/*
 * task.c
 *
 */


//#include "config.h"
#include "memory.h"
#include "task.h"
#include "type.h"


#pragma pack(1)
/**
 * @struct	_task
 * @brief	task queue define
 */
struct _task {
	void (*fun)(void);				///< task function
	unsigned short loop_time;		///< interval during loop
	unsigned long update_time;		///< record the last time
};

/*
 * global value
 */
static void (*super_task[SUPER_TASK_NUM])(void);	///<super task queue
static struct _task task[TASK_NUM];					///<task queue

/**
 * @brief	switch two task
 * @param	p1 task 1
 * @param	p2 task 2
 * @return	none
 * @note
 */
static void TaskSwitch(struct _task *p1, struct _task *p2)
{
	struct _task temp;
	/* swap */
	temp.fun			= p2->fun;
	temp.loop_time		= p2->loop_time;
	temp.update_time	= p2->update_time;
	p2->fun				= p1->fun;
	p2->loop_time		= p1->loop_time;
	p2->update_time		= p1->update_time;
	p1->fun				= temp.fun;
	p1->loop_time		= temp.loop_time;
	p1->update_time		= temp.update_time;
}

/** @ingroup task Cargo Task handler
 * @{
 */

/**
 * @brief	Sort task Queue
 * @param	none
 * @return	none
 * @note	Can't call this function in ISR
 */
void TaskSch(void)
{
	int i, j;

	/* Bubble Sort */
	for (i = 0; i < gTaskJoinNum; i++)
	{
		for (j = i + 1; j < gTaskJoinNum; j++)
		{
			if (task[i].loop_time > task[j].loop_time)
			{
				TaskSwitch(&task[i], &task[j]);
			}
		}
	}

}

/**
 * @brief	Reset Task queue & SuperTask queue to default
 * @param	none
 * @return	none
 * @note
 */
void TaskInit(void)
{
	int i;

	gTaskJoinNum = 0;
	/* clean task queue */
	for (i = 0; i < TASK_NUM; i++)
	{
		task[i].fun			= NULL;
		task[i].loop_time	= 0;
		task[i].update_time	= 0;
	}
	for (i = 0; i < SUPER_TASK_NUM; i++)
	{
		super_task[i] = NULL;
	}
}

/**
 * @brief	Add a new task into the queue
 * @param	fun			task function
 * @param	loop_time	loop time
 * @return	TASK_OK/TASK_ERR_BUF_FULL
 * @note	Can't call this function in ISR
 */
int TaskAdd(void (*fun)(void), unsigned short loop_time)
{
	int err = TASK_OK;

	/* check task already exist */
	if (TaskGetIdx(fun) != TASK_NUM)
		return TASK_ERR_BUF_FULL;
	

	/* check task queue available */
	if (gTaskJoinNum < TASK_NUM)
	{
		/* ok */
		task[gTaskJoinNum].fun			= fun;
		task[gTaskJoinNum].loop_time	= loop_time;
		gTaskJoinNum++;
		if (gTaskJoinNum > gTaskMaxUse)
			gTaskMaxUse = gTaskJoinNum;
	}
	else
	{
		/* queue buffer full */
		// *pFlag_err_task_full = 1;
		err = TASK_ERR_BUF_FULL;
	}
	
	return err;
}

/**
 * @brief	return Task index in the queue
 * @param	none
 * @return	Task index
 * @note	return TASK_NUM mean not found
 */
int TaskGetIdx(void (*fun)(void))
{
	int i;

	for (i = 0; i < gTaskJoinNum; i++)
	{
		if (task[i].fun == fun)
		{
			/* match */
			return i;
		}
	}
	return TASK_NUM;

}

/**
 * @brief	remove a task from the queue
 * @param	fun			task function
 * @return	TASK_OK/TASK_ERR_NOT_FOUND
 * @note	Can't call this function in ISR
 */
int TaskDel(void (*fun)(void))
{
	int i;
	int err;
	
	i = TaskGetIdx(fun);
	if (i != TASK_NUM)
	{
		/* match */
		task[i].fun			= NULL;
		task[i].loop_time	= 0;
		task[i].update_time	= 0;
		err					= TASK_OK;
		gTaskJoinNum--;
		TaskSwitch(&task[i], &task[gTaskJoinNum]);
	}
	else
	{
		/* not match */
		err	= TASK_ERR_NOT_FOUND;
	}
	
	return err;
}

/**
 * @brief	remove a task from the queue
 * @param	fun			task function
 * @return	TASK_OK/TASK_ERR_BUF_FULL
 * @note	Can't call this function in ISR
 */
int SuperTaskAdd(void (*fun)(void))
{
	int i;
	int err = 0;

	for (i = 0; i < SUPER_TASK_NUM; i++)
	{
		if (super_task[i] == NULL)
		{
			super_task[i]= fun;
			err	= TASK_OK;
			break;
		}
	}
	if (i == SUPER_TASK_NUM)
	{
		err	= TASK_ERR_BUF_FULL;
	}
	
	return err;
}

/**
 * @brief	remove a task from the queue
 * @param	fun			task function
 * @return	TASK_OK/TASK_ERR_NOT_FOUND
 * @note	Can't call this function in ISR
 */
int SuperTaskDel(void (*fun)(void))
{
	int i;
	int err = 0;
	
	for (i = 0; i < SUPER_TASK_NUM; i++)
	{
		if (super_task[i] == fun)
		{
			super_task[i]= NULL;
			err	= TASK_OK;
			break;
		}
	}
	if (i == SUPER_TASK_NUM)
	{
		/* queue buffer full */
		err	= TASK_ERR_NOT_FOUND;
	}
	return err;
}

/**
 * @brief	execute Task function
 * @param	none
 * @return	none
 * @note
 */
extern int malloc_num;
void TaskRun(void)
{
	int i, j;
	unsigned long interval;
	
	for (i = 0;;)
	{
		/* super task */
		for (j = 0; j < SUPER_TASK_NUM; j++)
		{
			if (super_task[j] != NULL)
			{
				(*super_task[j])();
			}
		}
		/* task */
		if (task[i].fun != NULL)
		{
			interval = gSystick - task[i].update_time;
			if (interval >= task[i].loop_time)
			{
				/* Time up */
				(*task[i].fun)();
#if (DEBUG_MODE)
				if (malloc_num > 1)
				{
					Console_Write("TaskRun_err\r\n", 13);
					while(1) {}
				}
#endif
				task[i].update_time = gSystick;
			}
		}
		i++;
		if (i >= gTaskJoinNum)
		{
			i = 0;
		}
	}
}
/** @} */